#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>


#define BUF_SIZE 1024

/**
 * sem
 * 0 --- буфер занят IN 
 * 1 --- буфер занят OUT
 * 2 3 --- передача файла
 */



#define EMPTY 2
#define FULL  3

#define IN_BLOCK  0
#define OUT_BLOCK 1


int main( int argc, char **argv)
{
	if ( argc != 2)
	{
		printf("Wrong arguments\n");
		exit(0);
	}
	
	int fd_in = open( (char*)argv[1], O_RDONLY);
	if ( fd_in == -1)
	{
		printf("Can't open file: %s",(char*)argv[1]);
		exit(0);
	}
///////////////////////////////////////////////
	open( ".for_keys", O_CREAT | O_EXCL , 0644);
	
	key_t key_sem_common = ftok( ".for_keys", 10);
	key_t key_shm_common = ftok( ".for_keys", 20);

	int sem_id_common = semget( key_sem_common ,4,IPC_CREAT | 0644);
///////////////
	int shm_id_common = shmget( key_shm_common, BUF_SIZE + sizeof(int), IPC_CREAT | 0644);
///////////////
	void  *ptr_shm = shmat( shm_id_common, NULL, NULL);
	char  *ptr_shm_buffer = (char* )ptr_shm + sizeof(int);
	int   *ptr_shm_count  = (int*  )ptr_shm    ;
///////////////////////////////////////////////	
	struct sembuf sem_buf[ 10];
		sem_buf[0].sem_num = IN_BLOCK; sem_buf[0].sem_op =  0;   sem_buf[0].sem_flg = NULL;
		sem_buf[1].sem_num = IN_BLOCK; sem_buf[1].sem_op =  1;   sem_buf[1].sem_flg = SEM_UNDO;
		sem_buf[2].sem_num = FULL;     sem_buf[2].sem_op =  3;   sem_buf[2].sem_flg = NULL;
		sem_buf[3].sem_num = FULL;     sem_buf[3].sem_op = -3;   sem_buf[3].sem_flg = SEM_UNDO;
		
		
	semop( sem_id_common, &sem_buf, 4);
	
	int read_count;	
	do
	{
		sem_buf[0].sem_num = EMPTY; sem_buf[0].sem_op =  -2;   sem_buf[0].sem_flg = NULL;		
		semop( sem_id_common, sem_buf,1);
			if ( semctl( sem_id_common, EMPTY , GETVAL) != 0)
			{
				printf("Writer is killed\n");
				
				sem_buf[0].sem_num = EMPTY; sem_buf[0].sem_op =  -1;   sem_buf[0].sem_flg = NULL;
				semop( sem_id_common, sem_buf, 1);
				
				sem_buf[0].sem_num = FULL;     sem_buf[0].sem_op =  3;   sem_buf[0].sem_flg = SEM_UNDO;
				sem_buf[1].sem_num = FULL;     sem_buf[1].sem_op = -3;   sem_buf[1].sem_flg = NULL;
				semop( sem_id_common, sem_buf, 2);
				
				exit(0);
			}
			
			read_count = read( fd_in, ptr_shm_buffer, BUF_SIZE);
			if ( read_count == -1)
			{
				perror("In read error");
				exit(0);
			}
			else
			{
				*ptr_shm_count = read_count;		
			}
			sem_buf[0].sem_num = FULL; sem_buf[0].sem_op =  2;   sem_buf[0].sem_flg = NULL;		
			semop( sem_id_common , sem_buf, 1);
	}
	while( read_count);
/////////////////
		sem_buf[0].sem_num = FULL;     sem_buf[0].sem_op =  3;   sem_buf[0].sem_flg = SEM_UNDO;
		sem_buf[1].sem_num = FULL;     sem_buf[1].sem_op = -3;   sem_buf[1].sem_flg = NULL;
		
	semop( sem_id_common, sem_buf, 2);
////////
}